package ru.dc.gui.swing;

import org.jdesktop.observablecollections.ObservableCollections;
import org.jdesktop.swingx.JXTable;
import org.jdom.JDOMException;
import ru.dc.Application;
import ru.dc.gui.swing.util.IconLoader;
import ru.dc.object.FavouriteHub;
import ru.dc.object.ServerInfo;
import ru.dc.util.PublicHubListUtils;
import ru.swing.html.Binder;
import ru.swing.html.DomModel;
import ru.swing.html.ModelElement;
import ru.swing.html.css.SelectorGroup;

import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.util.*;

/**
 * <pre>
 * User: Penkov Vladimir
 * Date: 08.11.2010
 * Time: 12:33:05
 * </pre>
 */
public class PublicHubs extends Tab {

    private JXTable hubsTable;

    @ModelElement("sourcesModel")
    private SourcesModel sourcesModel = new SourcesModel();

    @ModelElement("hubsModel")
    private HubsModel hubsModel = new HubsModel();

    private DomModel model;


    public PublicHubs() {

        hubsTable = new JXTable();

        HashMap<SelectorGroup, JComponent> substitutions = new HashMap<SelectorGroup, JComponent>();
        substitutions.put(new SelectorGroup("#hubs"), hubsTable);
        try {
            model = Binder.bind(this, true, substitutions);
        } catch (JDOMException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }


        clear();

        getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK), "retrieve");
        getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK), "edit");
        getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK), "clear");


        refreshSources();
    }


    /**
     * Invoked before showing popup. Selects row under the cursor
     * @param e
     */
    public void preparePopup(MouseEvent e) {
        //will work in swinghtmltemplate 0.7
        int viewRow = hubsTable.rowAtPoint(e.getPoint());
        hubsTable.getSelectionModel().setSelectionInterval(viewRow, viewRow);
    }


    /**
     * Invoked on table dbl click.
     * @see #connect()
     */
    public void onTableDblClick() {
        connect();
    }


    /**
     * Invoked when 'connect' action is triggered. (e.g. in popupMenu).
     * Connects to the selected hub. Do nothing if no hub is selected.
     */
    public void connect() {
        if (hubsModel.getSelectedHub()==null) {
            return;
        }
        final ServerInfo si = hubsModel.getSelectedHub().getServerInfo();
        Application.getApplication().getConnectionManager().addHubConnection(si);
    }

    /**
     * Invoked when 'addToFav' action is triggered. (e.g. in popupMenu).
     * Adds selected hub to favourite hub list.
     */
    public void addToFavourites() {
        if (hubsModel.getSelectedHub()==null) {
            return;
        }
        final ServerInfo si = hubsModel.getSelectedHub().getServerInfo();
        FavouriteHub hub = new FavouriteHub();
        hub.setName(si.getAddress());
        hub.getServerInfo().setAddress(si.getAddress());
        hub.getServerInfo().setPort(si.getPort());
        Application.getApplication().getFavouritesService().addFavouriteHub(hub);
    }


    public void editSources() {
        PublicHubsSourcesDlg dlg = new PublicHubsSourcesDlg(Application.getApplication().getMainFrame());
        final Set<String> urlsSet = Application.getApplication().getPublicHubsService().getSourceUrls();
        dlg.setUrls(urlsSet.toArray(new String[urlsSet.size()]));

        if (dlg.execute() == JOptionPane.OK_OPTION) {
            String[] urls = dlg.getUrls();
            Application.getApplication().getPublicHubsService().setSourceUrls(urls);
            refreshSources();
        }
    }

    @Override
    public void onSelect() {
        super.onSelect();
        model.getTagById("retrieveBtn").getComponent().requestFocus();
    }

    /**
     * Fetches hubs from selected source.
     */
    public void refreshHubs() {
        if (sourcesModel.getSelectedSource()==null) {
            return;
        }
        final String url = sourcesModel.getSelectedSource();


        model.query("#retrieveBtn").removeClass("retrieveNormal").addClass("retrieveWork");
        JButton component = (JButton) model.query("#retrieveBtn").get(0).getComponent();
        //TODO http://code.google.com/p/swinghtmltemplate/issues/detail?id=21
        ImageIcon icon = IconLoader.getInstance().getIcon("ajax-loader.gif");
        component.setIcon(icon);

        new Thread(new Runnable() {
            public void run() {
                final Map<ServerInfo, Map<String, String>> hubs = PublicHubListUtils.getPublicHubList(url);
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        hubsModel.getHubs().clear();
                        for (ServerInfo si : hubs.keySet()) {
                            HubInfo hubInfo = new HubInfo(si, hubs.get(si));
                            hubsModel.getHubs().add(hubInfo);
                        }
                        alert();
                        PublicHubs.this.model.query("#retrieveBtn").removeClass("retrieveWork").addClass("retrieveNormal");
                    }
                });
            }
        }).start();
    }

    /**
     * Clears hubs model.
     */
    public void clear() {
        hubsModel.getHubs().clear();
    }


    public void refreshSources() {
        sourcesModel.getSources().clear();
        int c = 0;
        for (String url : Application.getApplication().getPublicHubsService().getSourceUrls()) {
            sourcesModel.getSources().add(url);
            c++;
        }

        final boolean b = c>0;
        model.getTagById("retrieveBtn").getComponent().setEnabled(b);
        model.getTagById("sources").getComponent().setEnabled(b);
    }


    /**
     * Holds sources for combobox model
     */
    public class SourcesModel {

        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);


        private java.util.List<String> sources = ObservableCollections.observableList(new ArrayList<String>());
        private String selectedSource;

        public List<String> getSources() {
            return sources;
        }

        public void setSources(List<String> sources) {
            this.sources = sources;
        }

        public String getSelectedSource() {
            return selectedSource;
        }

        public void setSelectedSource(String selectedSource) {
            Object old = this.selectedSource;
            this.selectedSource = selectedSource;
            pcs.firePropertyChange("selectedSource", old, selectedSource);
        }

        public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
            pcs.addPropertyChangeListener(propertyChangeListener);
        }

        public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) {
            pcs.removePropertyChangeListener(propertyChangeListener);
        }
    }


    /**
     * Holds hubs for table model.
     */
    public class HubsModel {

        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
        private java.util.List<HubInfo> hubs = ObservableCollections.observableList(new ArrayList<HubInfo>());
        private HubInfo selectedHub;

        public List<HubInfo> getHubs() {
            return hubs;
        }

        public void setHubs(List<HubInfo> hubs) {
            this.hubs = hubs;
        }

        public HubInfo getSelectedHub() {
            return selectedHub;
        }

        public void setSelectedHub(HubInfo selectedHub) {
            this.selectedHub = selectedHub;
        }

        public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
            pcs.addPropertyChangeListener(propertyChangeListener);
        }

        public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) {
            pcs.removePropertyChangeListener(propertyChangeListener);
        }

    }


    /**
     * Special wrapper for hub info. Used in hubsModel
     */
    public class HubInfo {

        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

        private ServerInfo serverInfo;
        private Map<String, String> attrs;

        public HubInfo(ServerInfo serverInfo, Map<String, String> attrs) {
            this.serverInfo = serverInfo;
            this.attrs = attrs;
        }

        public ServerInfo getServerInfo() {
            return serverInfo;
        }

        public Map<String, String> getAttrs() {
            return attrs;
        }


        public String getAddress() {
            return serverInfo.getAddress()+":"+serverInfo.getPort();
        }

        public String getName() {
            return attrs.get("Name");
        }

        public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
            pcs.addPropertyChangeListener(propertyChangeListener);
        }

        public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) {
            pcs.removePropertyChangeListener(propertyChangeListener);
        }
    }
}
